package com.hivesight.infra.log4j;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

public class AppEngineSender {
	
	private static Map<String, AppEngineSender> INSTANCES = new HashMap<String, AppEngineSender>();
	
	private String loggerEndPoint;
	private String apiKey;
	private String secretKey;
	
	private MultiThreadedHttpConnectionManager connManager = new MultiThreadedHttpConnectionManager();

	public synchronized static AppEngineSender getInstance(String apiKey, String secretKey, String loggerEndpoint) {
		String key = apiKey + ":" + secretKey;
		AppEngineSender instance = INSTANCES.get(key);
		if (instance == null) {
			instance = new AppEngineSender(apiKey, secretKey, loggerEndpoint);
			INSTANCES.put(key, instance);
		}
		return instance;
	}
	
	private AppEngineSender(String username, String password, String loggerEndpoint) {
		super();
		this.apiKey = username;
		this.secretKey = password;
		this.loggerEndPoint = loggerEndpoint;
	}

	/**
	 * Sends a message to AppEngine.
	 * @param text
	 * @param params 
	 * @throws Exception 
	 */
	public void send(List<NameValuePair> params) throws Exception {
		String address = InetAddress.getLocalHost().getHostAddress();
		if (address.indexOf("10.0.0") != -1 || address.indexOf("192.168.") != -1) {
			//params.add(new NameValuePair("dev_mode", "1"));
		}
		
        HttpClient client = new HttpClient(connManager);
        PostMethod post = new PostMethod(loggerEndPoint);

		// add nonce		
		params.add(new NameValuePair("n", nonce()));

		for (NameValuePair param : params) {
			post.addParameter(param);
		}
		String sig = sign(params);
		post.addParameter("sig", sig);
		post.addParameter("key", apiKey);
        
        try {
            int responseCode = client.executeMethod(post); 
            switch (responseCode) {
            	case 200:
            		break;
            	default:
            		throw new IOException("The server rejected your message with error code " + responseCode);
            }
		} finally {
        	post.releaseConnection();
        }
	}
	
	
	public static String nonce() {
		return DigestUtils.shaHex(UUID.randomUUID().toString());
	}
	
	public void sendSilent(List<NameValuePair> params) {
		try {
			send(params);
		} catch (Exception e) {
		}
	}

	/**
	 * Create the signature for this set of parameters
	 * 
	 * 1. Sort key=value pairs alphabetically by key
	 * 2. construct a string in the form: key1value1key2value2...keyNvalueN
	 * 3. append the secret key
	 * 4. run a SHA1 on the string
	 * 
	 * @param params
	 * @return
	 * @throws Exception
	 */
	private String sign(List<NameValuePair> params) throws Exception {
		StringBuilder sb = new StringBuilder();
		
		Collections.sort(params, new Comparator<NameValuePair>() {
			public int compare(NameValuePair p1, NameValuePair p2) {
				return p1.getName().compareTo(p2.getName());
			}			
		});
		
		for (NameValuePair pair : params) {
			sb.append(pair.getName()).append(pair.getValue());
		}
		sb.append(secretKey);
		String str = sb.toString();
		
		return DigestUtils.shaHex(str);
	}
	
}
